/*
 * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: MulanPSL-2.0
 */
#![allow(clippy::unnecessary_unwrap)]
use crate::common::*;

use crate::process_hooks_unsetenv;

// #define SUDO_HOOK_RET_STOP	1	/* stop hook processing for this type */
// #define SUDO_HOOK_RET_ERROR	-1	/* error */
pub const SUDO_HOOK_RET_STOP: libc::c_int = 1;
pub const SUDO_HOOK_RET_ERROR: libc::c_int = -1;

pub type sudo_fn_putenv_t = Option<unsafe extern "C" fn(*mut libc::c_char) -> libc::c_int>;
pub type sudo_fn_setenv_t = Option<
    unsafe extern "C" fn(*const libc::c_char, *const libc::c_char, libc::c_int) -> libc::c_int,
>;
pub type sudo_fn_unsetenv_t = Option<unsafe extern "C" fn(*mut libc::c_void) -> libc::c_int>;

extern "C" {
    //c rust:　　extern,static,?
    static mut environ: *mut *mut libc::c_char;
    fn strncmp(__s1: *const libc::c_char, __s2: *const libc::c_char, __n: size_t) -> libc::c_int;
    fn strchr(__s: *const libc::c_char, __c: libc::c_int) -> *mut libc::c_char;
    fn reallocarray(__ptr: *mut libc::c_void, __nmemb: size_t, __size: size_t)
        -> *mut libc::c_void;
    fn memcpy(
        __restrict: *mut libc::c_void,
        __src: *const libc::c_void,
        __n: size_t,
    ) -> *mut libc::c_void;
    fn __errno_location() -> *mut libc::c_int;
    fn strlen(__s: *const libc::c_char) -> size_t;
    fn malloc(__size: size_t) -> *mut libc::c_void;
    fn free(__ptr: *mut libc::c_void);
    fn sudo_dso_findsym_v1(
        handle: *mut libc::c_void,
        symbol: *const libc::c_char,
    ) -> *mut libc::c_void;
    fn process_hooks_setenv(
        name: *const libc::c_char,
        value: *const libc::c_char,
        overwrite: libc::c_int,
    ) -> libc::c_int;
    fn process_hooks_putenv(string: *mut libc::c_char) -> libc::c_int;
    fn process_hooks_getenv(
        name: *const libc::c_char,
        value: *mut *mut libc::c_char,
    ) -> libc::c_int;
}

static mut priv_environ: *mut *mut libc::c_char = 0 as *mut *mut libc::c_char;

#[no_mangle]
//pub unsafe extern "C" fn getenv_unhooked(mut name: *const libc::c_char) -> *mut libc::c_char {
pub fn getenv_unhooked(mut name: *const libc::c_char) -> *mut libc::c_char {
    unsafe {
        let mut ep: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
        let mut val: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
        let mut namelen: size_t = 0 as size_t;

        while *name.offset(namelen as isize) != '\u{0}' as libc::c_char
            && *name.offset(namelen as isize) != '=' as libc::c_char
        {
            namelen += 1;
        }
        ep = environ;
        while !(*ep).is_null() {
            if strncmp(*ep, name, namelen) == 0
                && *(*ep).offset(namelen as isize) == '=' as libc::c_char
            {
                val = (*ep).offset(namelen as isize).offset(1_isize);
                break;
            }
            ep = ep.offset(1);
        }
        val
    }
}

#[no_mangle]
pub fn getenv(mut name: *const libc::c_char) -> *mut libc::c_char {
    unsafe {
        let mut val: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();

        match process_hooks_getenv(name, &mut val) {
            SUDO_HOOK_RET_STOP => val,
            SUDO_HOOK_RET_ERROR => std::ptr::null_mut::<libc::c_char>(),
            _ => getenv_unhooked(name),
        }
    }
}

#[no_mangle]
//unsafe extern "C" fn rpl_putenv(mut string: *mut libc::c_char) -> libc::c_int {
fn rpl_putenv(mut string: *mut libc::c_char) -> libc::c_int {
    let mut ep: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
    let mut len: size_t = 0 as size_t;
    let mut found: bool = false;

    unsafe {
        len = (strchr(string, '=' as i32).offset_from(string) + 1) as size_t;

        ep = environ;
        while !(*ep).is_null() {
            if strncmp(string, *ep, len) == 0 {
                *ep = string;
                found = true;
            }
            ep = ep.offset(1);
        }

        if found {
            while !(*ep).is_null() {
                if strncmp(string, *ep, len) == 0 {
                    let mut cur: *mut *mut libc::c_char = ep;
                    loop {
                        *cur = *cur.offset(1);

                        if !(*cur).is_null() {
                            break;
                        }
                        cur = cur.offset(1);
                    }
                } else {
                    ep = ep.offset(1);
                }
            }
        }

        if !found {
            //offset_from() :a - b
            let mut env_len: size_t = ep.offset_from(environ) as size_t;
            let mut envp: *mut *mut libc::c_char = reallocarray(
                priv_environ as *mut libc::c_void,
                env_len + 2 as libc::c_ulong,
                ::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong,
            ) as *mut *mut libc::c_char;
            if envp.is_null() {
                return -1;
            }
            if environ != priv_environ {
                //乘 wrapping_mul
                memcpy(
                    envp as *mut libc::c_void,
                    environ as *mut libc::c_void,
                    env_len * (::std::mem::size_of::<*mut libc::c_char>() as size_t),
                );
            };
            let mut tmp = env_len + 1;
            *envp.offset(tmp as isize) = string;
            *envp.offset(env_len as isize) = std::ptr::null_mut::<libc::c_char>();
            environ = envp;
            priv_environ = environ;
        }
    }
    0
}

#[no_mangle]
fn putenv_unhooked(mut string: *mut libc::c_char) -> libc::c_int {
    //wait  expect?  None ? b"str"\0?
    unsafe {
        let mut fn_1: sudo_fn_putenv_t = None;

        fn_1 = ::std::mem::transmute::<*mut libc::c_void, sudo_fn_putenv_t>(sudo_dso_findsym_v1(
            -(1 as libc::c_int) as *mut libc::c_void,
            b"putenv\0" as *const u8 as *const libc::c_char,
        ));

        if fn_1.is_some() {
            return fn_1.expect("is null func pointer")(string);
        }
        rpl_putenv(string)
    }
}

#[no_mangle]
fn putenv(mut string: *mut libc::c_char) -> libc::c_int {
    unsafe {
        match process_hooks_putenv(string) {
            SUDO_HOOK_RET_STOP => 0 as libc::c_int,
            SUDO_HOOK_RET_ERROR => -(1 as libc::c_int),
            _ => putenv_unhooked(string),
        }
    }
}

#[no_mangle]
fn rpl_setenv(
    mut var: *const libc::c_char,
    mut val: *const libc::c_char,
    mut overwrite: libc::c_int,
) -> libc::c_int {
    let mut envstr: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut dst: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut src: *const libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut esize: size_t = 0 as size_t;

    unsafe {
        if var.is_null() || *var == '\u{0}' as libc::c_char {
            *__errno_location() = EINVAL as libc::c_int;
            return -1;
        }

        src = var;
        while *src != '\u{0}' as libc::c_char && *src != '=' as libc::c_char {
            src = src.offset(1);
        }
        //offset_from?wrapping_add?
        esize = (src.offset_from(var) as libc::c_long as size_t).wrapping_add(2);

        if !val.is_null() {
            esize = esize.wrapping_add(strlen(val));
        }

        envstr = malloc(esize) as *mut libc::c_char;
        if envstr.is_null() {
            return -1;
        }

        src = var;
        dst = envstr;
        while *src != '\u{0}' as libc::c_char && *src != '=' as libc::c_char {
            src = src.offset(1);
            dst = dst.offset(1);
            *dst = *src;
        }

        dst = dst.offset(1);
        *dst = '=' as libc::c_char;

        if !val.is_null() {
            src = val;
            while *src as libc::c_int != '\u{0}' as i32 {
                src = src.offset(1);
                dst = dst.offset(1);
                *dst = *src;
            }
        }
        *dst = '\u{0}' as libc::c_char;

        if overwrite == 0 && !getenv(var).is_null() {
            free(envstr as *mut libc::c_void);
            return 0;
        }

        if rpl_putenv(envstr) == -1 {
            free(envstr as *mut libc::c_void);
            return -1;
        }
    }
    0
}

//fn setenv_unhooked
fn setenv_unhooked(
    mut var: *const libc::c_char,
    mut val: *const libc::c_char,
    mut overwrite: libc::c_int,
) -> libc::c_int {
    let mut fn_2: sudo_fn_setenv_t = None;
    unsafe {
        fn_2 = ::std::mem::transmute::<*mut libc::c_void, sudo_fn_setenv_t>(sudo_dso_findsym_v1(
            -(1 as libc::c_int) as *mut libc::c_void,
            b"setenv\0" as *const u8 as *const libc::c_char,
        ));

        if fn_2.is_some() {
            return fn_2.expect("non-null function pointer")(var, val, overwrite);
        }
        rpl_setenv(var, val, overwrite)
    }
}

#[no_mangle]
pub fn setenv(
    mut var: *const libc::c_char,
    mut val: *const libc::c_char,
    mut overwrite: libc::c_int,
) -> libc::c_int {
    unsafe {
        match process_hooks_setenv(var, val, overwrite) {
            SUDO_HOOK_RET_STOP => 0,
            SUDO_HOOK_RET_ERROR => -1,
            _ => setenv_unhooked(var, val, overwrite),
        }
    }
}

//
#[no_mangle]
fn rpl_unsetenv(mut var: *const libc::c_char) -> libc::c_int {
    unsafe {
        let mut _ep: *mut *mut libc::c_char = environ;
        let mut _len: size_t = 0 as size_t;

        if var.is_null() || *var == '\u{0}' as libc::c_char || !(strchr(var, '=' as i32)).is_null()
        {
            *__errno_location() = EINVAL;
            return -1;

            _len = strlen(var);

            while !(*_ep).is_null() {
                if strncmp(var, *_ep, _len) == 0 as libc::c_int
                    && *(*_ep).offset(_len as isize) as libc::c_int == '=' as i32
                {
                    let mut cur: *mut *mut libc::c_char = _ep;
                    loop {
                        *cur = *cur.offset(1);
                        if (*cur).is_null() {
                            break;
                        }
                        cur = cur.offset(1);
                    }
                } else {
                    _ep = _ep.offset(1);
                }
            }
        }
    }
    0
}

//252-275 wait write
fn unsetenv_unhooked(mut var: *const libc::c_char) -> libc::c_int {
    let mut ret: libc::c_int = 0 as libc::c_int;
    let mut fn_3: sudo_fn_unsetenv_t = None;
    unsafe {
        fn_3 = ::std::mem::transmute::<*mut libc::c_void, sudo_fn_unsetenv_t>(sudo_dso_findsym_v1(
            -(1 as libc::c_int) as *mut libc::c_void,
            b"unsetenv\0" as *const u8 as *const libc::c_char,
        ));

        if fn_3.is_some() {
            //ret = fn_3.expect("is not null func pointer")(var);
            ret = fn_3.expect("is null func pointer")(var as *mut libc::c_void) as libc::c_int;
        } else {
            ret = rpl_unsetenv(var);
        }
        ret
    }
}

#[no_mangle]
pub fn unsetenv(mut var: *const libc::c_char) -> libc::c_int {
    let mut ret: libc::c_int = 0 as libc::c_int;
    match process_hooks_unsetenv(var) {
        SUDO_HOOK_RET_STOP => ret = 0,
        SUDO_HOOK_RET_ERROR => ret = -1,
        _ => ret = unsetenv_unhooked(var),
    };

    ret
}
